use std::{
    collections::HashMap, fmt::Display, num::ParseIntError, ops::Sub, str::FromStr, sync::OnceLock,
};

const DAYS_IN_MONTH: &[i32] = &[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
static NEW_YEAR_DATES: OnceLock<HashMap<usize, Date>> = OnceLock::new();

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct Date(usize, usize, usize);

impl FromStr for Date {
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut _t = s.split('-');
        let year: usize = _t.next().unwrap().parse()?;
        let month: usize = _t.next().unwrap().parse::<usize>()?;
        let day: usize = _t.next().unwrap().parse()?;
        Ok(Self(year, month, day))
    }
}

impl Date {
    pub fn is_leap_year(&self) -> bool {
        if (self.0 % 4 == 0 && self.0 % 100 != 0) || (self.0 % 400 == 0) {
            true
        } else {
            false
        }
    }

    pub fn cur_days_in_year(&self) -> usize {
        let mut cur_days_in_year: usize = 0;
        for i in 0..self.1 - 1 {
            cur_days_in_year += DAYS_IN_MONTH[i] as usize;
        }
        cur_days_in_year += self.2;
        if self.is_leap_year() && self.1 > 1 {
            cur_days_in_year += 1;
        }
        cur_days_in_year
    }

    // 求当年第一天星期几
    pub fn cur_year_first_day_in_week(&self) -> usize {
        let mut res = 0;
        for i in 1..self.0 {
            if (i % 4 == 0 && i % 100 != 0) || (i % 400 == 0) {
                res += 366 % 7;
            } else {
                res += 365 % 7;
            }
        }
        res %= 7;
        res
    }

    // 冬至日期
    // pub fn count_winter_solstice(&self) -> usize {
    //     let c = if self.0 / 100 == 20 { 21.94 } else { 22.60 };
    //     ((self.0 % 100) as f32 * 0.2422 + c) as usize - (self.0 % 100) / 4
    // }
}

impl Sub for Date {
    type Output = usize;

    fn sub(self, rhs: Self) -> Self::Output {
        let mut res = 0;
        if rhs > self {
            return usize::MAX;
        }

        let mut oth = rhs;
        let mut _r_y = self.0 - oth.0;
        while _r_y > 0 {
            res += DAYS_IN_MONTH[oth.1 - 1] - oth.2 as i32;
            for m in oth.1..12 {
                res += DAYS_IN_MONTH[m];
            }

            if oth.is_leap_year() && oth.1 <= 2 {
                res += 1;
            }

            oth = Date(oth.0 + 1, 1, 1);
            res += 1;
            _r_y = self.0 - oth.0;
        }

        let _r_m = self.1 - oth.1;
        if _r_m > 0 {
            res += DAYS_IN_MONTH[oth.1 - 1] - oth.2 as i32;
            for m in oth.1..self.1 - 1 {
                res += DAYS_IN_MONTH[m];
            }
            if oth.is_leap_year() && oth.1 - 1 <= 2 && self.1 > 2 {
                res += 1;
            }
            res += self.2 as i32;
        } else {
            res += self.2 as i32 - oth.2 as i32;
        }
        res as usize
    }
}

impl Display for Date {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}-{}-{}", self.0, self.1, self.2)
    }
}

fn init_map() -> HashMap<usize, Date> {
    let mut map = HashMap::new();
    map.insert(2000, Date(2000, 1, 30));
    map.insert(2001, Date(2001, 2, 19));
    map.insert(2002, Date(2002, 2, 11));
    map.insert(2003, Date(2003, 1, 31));
    map.insert(2004, Date(2004, 1, 22));
    map.insert(2005, Date(2005, 2, 8));
    map.insert(2006, Date(2006, 1, 28));
    map.insert(2007, Date(2007, 2, 17));
    map.insert(2008, Date(2008, 2, 6));
    map.insert(2009, Date(2009, 1, 25));
    map.insert(2010, Date(2010, 2, 13));
    map.insert(2011, Date(2011, 2, 2));
    map.insert(2012, Date(2012, 1, 22));
    map.insert(2013, Date(2013, 2, 9));
    map.insert(2014, Date(2014, 1, 30));
    map.insert(2015, Date(2015, 2, 18));
    map.insert(2016, Date(2016, 2, 7));
    map.insert(2017, Date(2017, 1, 27));
    map.insert(2018, Date(2018, 2, 15));
    map.insert(2019, Date(2019, 2, 4));
    map.insert(2020, Date(2020, 1, 24));
    map.insert(2021, Date(2021, 2, 11));
    map.insert(2022, Date(2022, 1, 31));
    map.insert(2023, Date(2023, 1, 21));
    map.insert(2024, Date(2024, 2, 9));
    map.insert(2025, Date(2025, 1, 28));
    map.insert(2026, Date(2026, 2, 16));
    map.insert(2027, Date(2027, 2, 6));
    map.insert(2028, Date(2028, 1, 26));
    map.insert(2029, Date(2029, 2, 13));
    map.insert(2030, Date(2030, 2, 3));
    map.insert(2031, Date(2031, 1, 23));
    map.insert(2032, Date(2032, 2, 11));
    map.insert(2033, Date(2033, 1, 31));
    map.insert(2034, Date(2034, 2, 19));
    map.insert(2035, Date(2035, 2, 8));
    map.insert(2036, Date(2036, 1, 28));
    map.insert(2037, Date(2037, 2, 16));
    map.insert(2038, Date(2038, 2, 5));
    map.insert(2039, Date(2039, 1, 24));
    map.insert(2040, Date(2040, 2, 12));
    map
}

// 输出当前周是第几周，当年还剩多少天(不含当天)，距离过年(正月初一)还有多少天
pub fn time_info(time: &str) -> String {
    let new_year_dates = NEW_YEAR_DATES.get_or_init(|| init_map());
    let cur_date = time.parse::<Date>().unwrap();

    let days_in_years = if cur_date.is_leap_year() { 366 } else { 365 };
    let cur_days_in_year = cur_date.cur_days_in_year();

    // 当前周是第几周 ISO 8601?
    let first_thursday = if cur_date.cur_year_first_day_in_week() < 4 {
        4 - cur_date.cur_year_first_day_in_week()
    } else {
        7 - cur_date.cur_year_first_day_in_week() + 4
    };
    let some_weeks = if cur_date.cur_days_in_year() <= first_thursday {
        1
    } else if (cur_date.cur_days_in_year() - (first_thursday + 3)) % 7 == 0 {
        ((cur_date.cur_days_in_year() - (first_thursday + 3)) / 7 + 1) % 52
    } else {
        ((cur_date.cur_days_in_year() - (first_thursday + 3)) / 7 + 2) % 52
    };

    // 还剩几天
    let days_last = days_in_years - cur_days_in_year;
    let new_year_last = if new_year_dates.get(&cur_date.0).unwrap() >= &cur_date {
        *new_year_dates.get(&cur_date.0).unwrap() - cur_date
    } else {
        *new_year_dates.get(&(cur_date.0 + 1)).unwrap() - cur_date
    };

    format!("{},{},{}", some_weeks, days_last, new_year_last)
}
